package com.example.compiler;


import com.example.arouter_annotations.Arouter;
import com.example.arouter_annotations.bean.ARouterBean;
import com.example.compiler.utils.ProcessConfig;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.Filer;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

/**
 * @author sjl
 */ //在编译的时候执行此ArouterProccessor中的代码
@AutoService(Processor.class)
//观察的注解类型
@SupportedAnnotationTypes({"com.example.arouter_annotations.Arouter"})
//运行环境
@SupportedSourceVersion(SourceVersion.RELEASE_7)

// 接收 安卓工程传递过来的参数,在app模块的build.gradle有配置
@SupportedOptions("student")
public class ArouterProccessor extends AbstractProcessor {
    //通过此类可以拿到注解修饰的类的信息,从而进行操作
    private Elements elements;

    //用来判断类的类型
    private Types typeTool;
    //打印信息工具
    private Messager messager;
    //文件生成器,生成类等
    private Filer filer;

    private String aptPackage; // 各个模块传递过来的目录 用于统一存放 apt生成的文件
    //所有模块的path集合,编译的时候先用集合存储起来
    Map<String,List<ARouterBean>> mArouterBeansPathsMaps = new HashMap();

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elements = processingEnvironment.getElementUtils();
        typeTool = processingEnvironment.getTypeUtils();
        filer = processingEnvironment.getFiler();
        messager = processingEnvironment.getMessager();
        String student = processingEnvironment.getOptions().get("student");
        aptPackage = processingEnvironment.getOptions().get(ProcessConfig.APT_PACKAGE);
        messager.printMessage(Diagnostic.Kind.NOTE, "init =============="+student);

    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        messager.printMessage(Diagnostic.Kind.NOTE, "process ==============");
        //得到指定注解修饰的对象
        Set<? extends Element> elementsAnnotatedSet = roundEnvironment.getElementsAnnotatedWith(Arouter.class);
        messager.printMessage(Diagnostic.Kind.NOTE, "elementsAnnotatedSet size =============="+elementsAnnotatedSet.size());
        arouter(elementsAnnotatedSet);
        //createTest();
        //返回false表示还没做完,即每次build都会重新走process进行文件生成
        //返回true表示事情已经做完了,即如果已经build生成了文件,并且返回true,那么就算增加了注解,那么也不会重新走重新走process,生成文件
        return false;
    }
    private void createTest(){
        /**
         *
         *  模块一
         *              package com.example.helloworld;
         *
         *              public final class HelloWorld {
         *
         *              public static void main(String[] args) {
         *                  System.out.println("Hello, JavaPoet!");
         *              }
         *              }
         *
         */
        //先生成方法
        MethodSpec mainMethod =  MethodSpec.methodBuilder("main")//方法名称
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)//方法前的修饰
                .returns(void.class)//返回值
                .addParameter(String[].class, "args")//增加参数
                .addStatement("$T.out.println($S)",System.class ,"hello,javaPoet!")
                .build();//增加方法中的内容
        // 2.类
        TypeSpec testClass = TypeSpec.classBuilder("JavaPoetTest")
                .addMethod(mainMethod)
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .build();
        // 3.包
        JavaFile packagef = JavaFile.builder("com.javapoet.test", testClass).build();

        // 生成文件
        try {
            packagef.writeTo(filer);
        } catch (IOException e) {
            e.printStackTrace();
            messager.printMessage(Diagnostic.Kind.NOTE, "生成Test文件时失败，异常:" + e.getMessage());
        }
    }
    private void arouter( Set<? extends Element> elementsAnnotatedSet){
/*
        // 包信息
        String packageName = elementTool.getPackageOf(element).getQualifiedName().toString();

        // 获取简单类名，例如：MainActivity  MainActivity2  MainActivity3
        String className = element.getSimpleName().toString();
        messager.printMessage(Diagnostic.Kind.NOTE, "被@ARetuer注解的类有：" + className);

        // String className = element.getSimpleName().toString();

        // 目标：要生成的文件名称  MainActivity$$$$$$$$$ARouter
        String finalClassName = className + "$$$$$$$$$ARouter";

        *//**
         模板：
         public class MainActivity3$$$$$$$$$ARouter {

         public static Class findTargetClass(String path) {
         return path.equals("/app/MainActivity3") ? MainActivity3.class : null;
         }

         }
         *//*

        ARouter aRouter = element.getAnnotation(ARouter.class);

        // 1.方法
        MethodSpec findTargetClass = MethodSpec.methodBuilder("findTargetClass")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(Class.class)
                .addParameter(String.class, "path")
                // 方法里面的内容 return path.equals("/app/MainActivity3") ? MainActivity3.class : null;

                // 需要JavaPoet包装转型
                .addStatement("return path.equals($S) ? $T.class : null",
                        aRouter.path(),
                        ClassName.get((TypeElement) element))
                .build();

        // 2.类
        TypeSpec myClass = TypeSpec.classBuilder(finalClassName)
                .addMethod(findTargetClass)
                .addModifiers(Modifier.PUBLIC)
                .build();

        // 3.包
        JavaFile packagef = JavaFile.builder(packageName, myClass).build();

        // 开始生成
        try {
            packagef.writeTo(filer);
        } catch (IOException e) {
            e.printStackTrace();
            messager.printMessage(Diagnostic.Kind.NOTE, "生成" + finalClassName + "文件时失败，异常:" + e.getMessage());
        }
    }
        */
        //开始构建arouter框架
        //1.最上层抽象出路径接口,用于通过路径找到对应的class对象,名为PathARouter
        //2.再抽象出分组接口,用于不同的class文件进行归类,加快查找速度,名为GroupARouter,例如A模块的class,全部放在A组里面
        //3.每个模块都有一个PathARouter,想生成的代码如下
        /**
         * public class ARouter$$Path$$personal implements ARouterPath {
         *   @Override
         *   public Map<String, RouterBean> getPathMap() {
         *     Map<String, RouterBean> pathMap = new HashMap<>();
         *     pathMap.put("/personal/Personal_Main2Activity", RouterBean.create();
         *     pathMap.put("/personal/Personal_MainActivity", RouterBean.create());
         *     return pathMap;
         *   }
         * }
         */
        //开始编写代码

        // 通过Element工具类，参数为activity的包名+类名,可以得到Activity
        TypeElement activityType = elements.getTypeElement(ProcessConfig.ACTIVITY_PACKAGE);
        messager.printMessage(Diagnostic.Kind.NOTE,"activityType ="+activityType);

        // 显示类信息（获取被注解的节点，类节点）这也叫自描述 Mirror
        TypeMirror activityMirror = activityType.asType();
        for (Element element:elementsAnnotatedSet) {
            String name =  element.getSimpleName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE,"name ="+name);
            messager.printMessage(Diagnostic.Kind.NOTE,"type ="+element.asType());
            Arouter arouter = element.getAnnotation(Arouter.class);
            //判断修饰的类型是不是activity的子类
            boolean isSubtype = typeTool.isSubtype(element.asType(), activityMirror);
            //如果不是activity类型,抛出异常
            if (!isSubtype){
                throw new RuntimeException("Arouter注解只能用于activity上");
            }
            ARouterBean aRouterBean = new ARouterBean.Builder().setGroup(arouter.group()).setPath(arouter.path()).setTypeEnum(ARouterBean.TYPE_ENUM.ACTIVITY_TYPE).build();
            messager.printMessage(Diagnostic.Kind.NOTE,"aRouterBean ="+aRouterBean);
            List<ARouterBean> aRouterBeanList = mArouterBeansPathsMaps.get(aRouterBean.getGroup());

            //如果集合中没有此group的集合
            if (aRouterBeanList == null || aRouterBeanList.isEmpty()) {
                messager.printMessage(Diagnostic.Kind.NOTE,"aRouterBeanList size ="+aRouterBeanList);
                aRouterBeanList = new ArrayList<>();
                aRouterBeanList.add(aRouterBean);
                mArouterBeansPathsMaps.put(aRouterBean.getPath(),aRouterBeanList);
            }else {
                messager.printMessage(Diagnostic.Kind.NOTE,"aRouterBeanList size ="+aRouterBeanList.size());
                aRouterBeanList.add(aRouterBean);
            }
            //以上已经把注解修饰的类加到集合中去了
            //因为面向对象思想,所以需要先生成接口,然后再用实例化对象继承接口
            //1.先通过javaPoet的api找到已经定义好的接口类
            TypeElement pathElement = elements.getTypeElement(ProcessConfig.AROUTER_API_PATH);
            messager.printMessage(Diagnostic.Kind.NOTE,"api_path ="+ProcessConfig.AROUTER_API_PATH);
            messager.printMessage(Diagnostic.Kind.NOTE,"pathElement ="+pathElement);
            try {
                createPathClass(pathElement);
            } catch (IOException e) {
                messager.printMessage(Diagnostic.Kind.NOTE, "create file exception:"+e.getMessage() );
                throw new RuntimeException(e);
            }

        }
    }
    private void createPathClass(TypeElement pathElement) throws IOException {
      if (mArouterBeansPathsMaps==null || mArouterBeansPathsMaps.isEmpty()){
          return;
      }
        //生成返回值Map<String, RouterBean>
        //因为所有的class类型,必须通过javaPoet的包装才能生成,满足JavaPoet的入参语法
        ParameterizedTypeName typeName = ParameterizedTypeName.get(
                ClassName.get(Map.class), // Map
                ClassName.get(String.class),// Map<String,
                ClassName.get(ARouterBean.class)// Map<String, RouterBean>
        );

        //先生成方法,因为每个path就要生成一个实现类,所以需要遍历
        for (Map.Entry<String, List<ARouterBean>> stringListEntry : mArouterBeansPathsMaps.entrySet()) {
            /**
             * public class ARouter$$Path$$personal implements ARouterPath {
             *   @Override
             *   public Map<String, RouterBean> getPathMap() {
             *     Map<String, RouterBean> pathMap = new HashMap<>()
             *     pathMap.put("/personal/Personal_Main2Activity", RouterBean.create(n个参数);
             *     pathMap.put("/personal/Personal_MainActivity", RouterBean.create(n个参数));
             *     return pathMap;
             *   }
             * }
             */
            //生成方法
            MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessConfig.PATH_METHOD_NAME)
                    .addAnnotation(Override.class)
                    .addModifiers(Modifier.PUBLIC)
                    //返回值
                    .returns(typeName);
            //添加方法内容
            /**
             * 添加的是此部门内容
             *  @Override
             *  public Map<String, RouterBean> getPathMap() {
             *   Map<String, RouterBean> pathMap = new HashMap<>();
             */
            methodBuilder.addStatement("$T<$T,$T> $N = new $T<>()",
                    Map.class,
                    String.class,
                    ARouterBean.class,
                    ProcessConfig.PATH_MAP_VAR,
                    HashMap.class
                    );
            //再添加往集合中添加元素的代码
            //public static ARouterBean create(String path, String group,TYPE_ENUM typeEnum, Class<?> classType) {
            //      return new ARouterBean(path, group,typeEnum,classType);
            //  }
            List<ARouterBean> pathList = stringListEntry.getValue();
            for (int i = 0; i < pathList.size(); i++) {
                ARouterBean aRouterBean = pathList.get(i);
                //生成调用方法的代码
                methodBuilder.addStatement("$N.put($S,$T.create($S $S $L $T.class",ProcessConfig.PATH_MAP_VAR,aRouterBean.getPath(),aRouterBean.getGroup(),aRouterBean.getTypeEnum(),aRouterBean.getClassType());
            }
            //添加方法返回值
            methodBuilder.addStatement("return $N",ProcessConfig.PATH_MAP_VAR);
            //添加class名称
            //类名
            // 路由组，PATH 最终要生成的 文件名
          String className = ProcessConfig.PATH_FILE_NAME +stringListEntry.getKey();
          messager.printMessage(Diagnostic.Kind.NOTE, "APT生成路由Path类文件：" +
                  aptPackage + "." + className);
          TypeSpec typeSpec = TypeSpec
                  .classBuilder(className)//添加类
                  .addSuperinterface(ClassName.get(pathElement))//添加继承的接口类
                  .addModifiers(Modifier.PUBLIC)
                  .addMethod(methodBuilder.build())
                  .build();
          //生成的包名,包名是在配置文件中配置的,会定义一个名为app_config.gradle的文件,里面配置了包名属性
            //可以在init方法中通过processingEnvironment.getOptions().get("配置的属性名称");获取包名
            //className.packageName(),
            JavaFile.builder(aptPackage,typeSpec).build().writeTo(filer);
        }



    }
}
